#include "ServerUtil.h"
#include "jwt-cpp/jwt.h"
#include <iostream>

#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
#include <exception>

#include "yaml-cpp/yaml.h"
#include "OCMain/Environment.h"

namespace OCPP
{
    const Json::Value ServerUtil::jwtDecode(String token)
    {
        Json::Value result;

        try {
            auto decoded = jwt::decode(token);
            result["token"] = decoded.get_token();
            
            Json::Value headerValue,payloadValue;
            Json::Reader parser;
            parser.parse(decoded.get_header().c_str(), headerValue);
            parser.parse(decoded.get_payload().c_str(), payloadValue);

            result["header"] = headerValue;
            result["payload"] = payloadValue;

            result["signature"] = decoded.get_signature();
            result["signature_base64"] = decoded.get_signature_base64();

            auto verifier = jwt::verify().allow_algorithm(jwt::algorithm::hs256{ "secret1" }).with_issuer("auth0");

            std::error_code ec;
            verifier.verify(decoded, ec);
            bool passed = (bool)!ec;
            result["verify"] = passed;
            result["verify_message"] = passed ? "" : ec.message();
        }
        catch (std::invalid_argument ex) {
            std::cout << ex.what() << "\n";
        }

        return result;
    }

    const String ServerUtil::jwtEncode(Json::Value& value)
    {
        auto token_1 = jwt::create()
            .set_issuer("auth0")
            .set_type("JWS")
            .set_payload_claim(std::string("sample"), jwt::claim(std::string("test")))
            .sign(jwt::algorithm::hs256{ "secret1" });
        return token_1;
    }

    const String ServerUtil::AES_Encode(String aesKey, String password, String ivc)
    {
        String key = aesKey;
        size_t size = key.size();
        if (size < 32) {
            key.resize(32);
            key.insert(size, 32 - size, '0');
        }

        int encLen = 0, outLen = 0;

        unsigned char encData[1024];
        EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
        EVP_CipherInit_ex(ctx, EVP_aes_256_ecb(), NULL, (uint8*)key.data(), NULL, AES_ENCRYPT);
        EVP_CipherUpdate(ctx, encData, &outLen, (unsigned char*)password.data(), password.length());
        encLen = outLen;
        EVP_CipherFinal(ctx, encData + outLen, &outLen);
        encLen += outLen;
        EVP_CIPHER_CTX_free(ctx);

        return StringUtil::hexEncode(String((const char*)encData, encLen));
    }

    const String ServerUtil::AES_Decode(String aesKey, String aseCode, String ivc)
    {
        String key = aesKey;
        size_t size = key.size();
        if (size < 32) {
            key.resize(32);
            key.insert(size, 32 - size, '0');
        }

        String code = StringUtil::hexDecode(aseCode);

        int decLen = 0, outlen = 0;
        unsigned char decData[1024];
        EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
        EVP_CipherInit_ex(ctx, EVP_aes_256_ecb(), NULL, (uint8*)key.data(), (uint8*)ivc.data(), AES_DECRYPT);
        EVP_CipherUpdate(ctx, decData, &outlen, (uint8*)code.data(), code.length());
        decLen = outlen;
        EVP_CipherFinal(ctx, decData + outlen, &outlen);
        decLen += outlen;
        EVP_CIPHER_CTX_free(ctx);
        decData[decLen] = '\0';
        return String((char*)&decData[0], decLen);
    }

    const String ServerUtil::jsonToString(Json::Value& value, const String indentation)
    {
        Json::StreamWriterBuilder wbuilder;
        wbuilder.settings_["indentation"] = indentation;
        wbuilder.settings_["emitUTF8"] = true;
        return Json::writeString(wbuilder, value);
    }

    const void ServerUtil::WaitHours(int h)
    {
        std::this_thread::sleep_for(std::chrono::hours(h));
    }

    const void ServerUtil::WaitMinutes(int m)
    {
        std::this_thread::sleep_for(std::chrono::minutes(m));
    }

    const void ServerUtil::WaitSeconds(int s)
    {
        std::this_thread::sleep_for(std::chrono::seconds(s));
    }
}
